package org.testis.testdriver.validation;

import org.testis.testdriver.*;
import org.testis.data.TestStepDataObject;
import org.testis.data.TestStepResultDataObject;
import org.testis.data.TestStepResultType;
import org.testis.services.TestStepExecutor;
import org.testis.testdriver.AtofEnumaration.AtofLogLevel;
import org.testis.testdriver.AtofEnumaration.DebuggingMode;
import org.testis.testdriver.resultsmatrix.ThreeDResultsMatrix;
import com.mallardsoft.tuple.Pair;
import com.mallardsoft.tuple.Tuple;
import java.util.Iterator;
import java.util.Set;

/**
 * This class is used to make validation according to Test Step
 * Startegy.
 * 
 * @author barisergun
 */
public class TestPartValidateTestStepStrategy extends TestPartValidateStrategy {

    public TestPartValidateTestStepStrategy(TestPart inUse) {
        super(inUse);
    }

    @Override
    public boolean validatePartResult(TestIterator testIterInRun,
            DebuggingMode mode,
            boolean partResult) {
        return true;
    }

    @Override
    protected boolean validateIfToContinue(boolean partResult) {
        return true;
    }

    @Override
    protected void validateFinalMatrix(StepContainer refCur) {
        throw new UnsupportedOperationException("Not supported yet.");
    }

    @Override
    public boolean validateFinalPartResult(TestIterator testIterInRun,
            DebuggingMode mode,
            boolean partResult) {
        TestStep refCurStep = (TestStep) testPartInRun;

        int invocationCountIndex = ((StepContainer) refCurStep.getParent()).getInvocationCountIndex();
        int childIndex = refCurStep.getParent().getTestPartList().
                indexOf(
                refCurStep);
        Short caseDataIndex =
                ((StepContainer) refCurStep.getParent()).getTestCaseDataIndex();

        ThreeDResultsMatrix ourMatrix = ((StepContainer) refCurStep.getParent()).getResultMatrix();


        if (!refCurStep.getTestDataList().isEmpty()) {
            if (!testPartInRun.getFailedConditions().isEmpty()) {
                // If the Test Step condition fails return true because
                // Empty TestSteps dont affect the Test Iteration But we must
                // log this result in 3D matrix as true as well so:
                ourMatrix.setResultIn3dArray(invocationCountIndex,
                        childIndex,
                        caseDataIndex,
                        true);
                Byte res = 3; //Log 3 when condition fails.
                logResultOtherThanPassOrFailAndFinish(
                        res,
                        TestStringConstants.getConditionFailedString() + "\n"
                        + testPartInRun.getFailedConditions().toString());

            } else {
                refCurStep.getParent().andTestPartResult(partResult);

                boolean hasNextData = refCurStep.hasNextDataObject();
                if (refCurStep.getDataArrayAppliesToCase() == false) {

                    String stepGuid = refCurStep.getPartGuidLongFromChild();
                    if (refCurStep.getParent().getContainerType()
                            == AtofEnumaration.TestPartType.TestStepGroup) {
                        stepGuid += refCurStep.getParent().getParent().
                                getPartGuidLongFromChild().replaceAll(refCurStep.getParent().
                                getParent().getPartGuid(),
                                "");
                    }
                    if (hasNextData) {
                        testIterInRun.previous();
                        NotificationSubjectSingleton.getInstance().notifyLogMessages(
                                refCurStep.getPartName(),
                                AtofLogLevel.INFO,
                                TestStringConstants.getTestStepMoreDataString(),
                                stepGuid);
                    } else {// hasNextData

                        waitAsyncSteps();
                        String caseSuccessLog = " Expected Test Case success percentage:"
                                + ((StepContainer) refCurStep.getParent()).getCaseSuccessPercentage();
                        int noOfSuccessFullData = 0;
                        int dataObjectCount = refCurStep.getTestDataList().size();
                        for (int u = 0; u < dataObjectCount; u++) {
                            if (Tuple.get2(refCurStep.getStepResultSet().get(u + 1))
                                    == true) {
                                noOfSuccessFullData++;
                            }
                        }
                        int successPercentage = 100;
                        if (dataObjectCount != 0) {
                            successPercentage = (noOfSuccessFullData * 100)
                                    / dataObjectCount;
                        }
                        boolean isSuccessfull = successPercentage
                                >= refCurStep.getM_Step_SuccessPercentage();

                        Pair<TestStepDataObject, Boolean> resCasePair =
                                new Pair<TestStepDataObject, Boolean>(null,
                                isSuccessfull);


                        refCurStep.getCaseResultSet().put(caseDataIndex,
                                resCasePair);

                        if (ourMatrix != null) {


                            if (refCurStep.getTestDataList().size() > 0) {
                                ourMatrix.setResultIn3dArray(invocationCountIndex,
                                        childIndex,
                                        caseDataIndex,
                                        isSuccessfull);
                            }

                        }

                        NotificationSubjectSingleton.getInstance().notifyLogMessages(
                                refCurStep.getPartName(),
                                AtofLogLevel.INFO,
                                "TestSteps current TestCase specific result set: "
                                + refCurStep.getCaseResultSet()
                                + caseSuccessLog,
                                stepGuid);

                        int retryCount = refCurStep.getRetryCount();
                        int retryOnFailCount = refCurStep.getRetryOnFailCount();

                        if (retryCount < retryOnFailCount && !isSuccessfull) {
                            refCurStep.setRetryCount(refCurStep.getRetryCount() + 1);

                            testIterInRun.previous();
                            NotificationSubjectSingleton.getInstance().notifyLogMessages(
                                    refCurStep.getPartName(),
                                    AtofLogLevel.INFO,
                                    "TestStep iterated back to itself for RetryCount:"
                                    + String.valueOf(refCurStep.getRetryCount()),
                                    stepGuid);
                        } else {
                            //Reset retrycount
                            refCurStep.setRetryCount(0);
                            if ((refCurStep.getIsCritical() == true) && (isSuccessfull
                                    == false)) {
                                NotificationSubjectSingleton.getInstance().notifyLogMessages(
                                        refCurStep.getPartName(),
                                        AtofLogLevel.WARN,
                                        " is a critical Test Step and failed so skipping "
                                        + "the remaining.",
                                        stepGuid);
                                return false;
                            }
                        }

                    }
                } else if (hasNextData
                        && (refCurStep.getDataArrayAppliesToCase() == true)) {
                    ((StepContainer) refCurStep.getParent()).setRetryCaseWithOtherData(
                            true);
                }
            }
        }// If the TestDataList is empty we stil return true because
        // Empty TestSteps dont affect the Test Iteration But we must
        // log this result in 3D matrix as true as well so:
        else {
            ourMatrix.setResultIn3dArray(invocationCountIndex,
                    childIndex,
                    caseDataIndex,
                    true);
            Byte res = 2;
            logResultOtherThanPassOrFailAndFinish(
                    res,
                    TestStringConstants.getEmptyTestStepString());
        }

        return true;
    }

    private void logResultOtherThanPassOrFailAndFinish(Byte resByte,
            String logFailMessage) {


        String curStepName = testPartInRun.getPartName();
        String curStepGuid = testPartInRun.getPartGuid()
                + testPartInRun.getParent().getPartGuidLongFromChild().replaceAll(
                testPartInRun.getParent().getPartGuid(), "");
        String curStepParentGuid = testPartInRun.getParent().getPartGuid()
                + testPartInRun.getParent().getPartGuidLongFromChild().replaceAll(
                testPartInRun.getParent().getPartGuid(), "");

        if (testPartInRun.getParent().getContainerType()
                == AtofEnumaration.TestPartType.TestStepGroup) {
            curStepGuid = curStepGuid
                    + testPartInRun.getParent().getParent().
                    getPartGuidLongFromChild().replaceAll(
                    testPartInRun.getParent().getParent().getPartGuid(), "");

            curStepParentGuid = curStepParentGuid
                    + testPartInRun.getParent().getParent().
                    getPartGuidLongFromChild().replaceAll(
                    testPartInRun.getParent().getParent().getPartGuid(), "");

        }

        NotificationSubjectSingleton.getInstance().notifyLogMessages(
                curStepName,
                AtofLogLevel.INFO,
                logFailMessage,
                curStepGuid);

        NotificationSubjectSingleton.getInstance().notifyLogHeaderMessages(
                Integer.parseInt(
                testPartInRun.getPartId()),
                AtofEnumaration.TestPartType.TestStep.ordinal(),
                resByte,
                curStepParentGuid,
                curStepName,
                curStepGuid,
                ExecutionGuidProvider.createInstance().
                getExistingExecId());

        NotificationSubjectSingleton.getInstance().notifyLogMessages(
                curStepName,
                AtofLogLevel.INFO,
                TestStringConstants.getEndedExecutionString(),
                curStepGuid);


    }

    private void waitAsyncSteps() {
        TestStep curStep = (TestStep) testPartInRun;

        if (curStep.getAsynchCall()) {

            String parentStepGuid = curStep.getPartGuidLongFromChild();

            if (curStep.getParent().getContainerType()
                    == AtofEnumaration.TestPartType.TestStepGroup) {
                // In this case we must also add the parent TestCase Guid Postfix

                parentStepGuid += curStep.getParent().getParent().
                        getPartGuidLongFromChild().replaceAll(curStep.getParent().
                        getParent().getPartGuid(),
                        "");
            }

            Set<Integer> keys = curStep.getStepResultSet().keySet();
            Iterator<Integer> setIter = keys.iterator();

            while (setIter.hasNext()) {
                Integer mapKey = setIter.next();
                TestStepDataObject asyncData = Tuple.get1(curStep.getStepResultSet().get(
                        mapKey));
                boolean asyncResult = false;

                TestStepResultType asyncResultType;
                if (!asyncData.isConditionFailed()) {
                    if (!asyncData.isStopExecution()) {
                        TestStepResultDataObject result = TestStepExecutor.getResultReciever(false).
                                recieve(
                                asyncData.getMessageId(),
                                curStep.getTimeOut() * 1000);

                        if (result != null) {
                            asyncResultType = result.getResultType();
                        } else {
                            asyncResultType = waitAsyncTimeOutResult(asyncData);
                        }
                        asyncResult = asyncResultType
                                == asyncData.getExpectedResultType();
                    } else {
                        asyncResultType = TestStepResultType.FAILURE;
                        asyncResult = false;
                    }
                    NotificationSubjectSingleton.getInstance().notifyLogMessages(
                        curStep.getPartName(),
                        AtofLogLevel.INFO,
                        "Aynch Test Step Result: " + asyncResultType
                        + "; Expected Result: " + asyncData.getExpectedResultType(),
                        asyncData.getMessageId());
                }else{
                    asyncResultType = TestStepResultType.PASS;
                        asyncResult = true;
                }
                




                Byte resByte = 1;
                if (!asyncResult) {
                    resByte = 0;
                }
                if(asyncData.isConditionFailed()){
                    resByte = 3;
                }

                String dataObjName = TestStringConstants.getDataObjectPreFixString()
                        + asyncData.getTestStepDataName()
                        + curStep.getTsgSpecificRetryCount();

                NotificationSubjectSingleton.getInstance().notifyLogHeaderMessages(
                        Integer.parseInt(asyncData.getTestStepDataId()),
                        9,
                        resByte,
                        parentStepGuid,
                        dataObjName,
                        asyncData.getMessageId(),
                        ExecutionGuidProvider.createInstance().
                        getExistingExecId());

                Pair<TestStepDataObject, Boolean> resSetCurPair =
                        new Pair<TestStepDataObject, Boolean>(asyncData,
                        asyncResult);
                curStep.getStepResultSet().put(mapKey,
                        resSetCurPair);
            }
            String stepSuccessLog = " Expected Test Step success percentage:"
                    + curStep.getM_Step_SuccessPercentage();
            NotificationSubjectSingleton.getInstance().notifyLogMessages(
                    curStep.getPartName(),
                    AtofLogLevel.INFO,
                    "Asynch TestSteps current TestStep"
                    + " specific result set: "
                    + curStep.getStepResultSet()
                    + stepSuccessLog,
                    parentStepGuid);
        }
    }

    private TestStepResultType waitAsyncTimeOutResult(
            TestStepDataObject asyncData) {
        TestStepResultDataObject result = null;
        TestStepResultType stepResultType = TestStepResultType.NULL;
        asyncData.setAsynchCall(false);
        asyncData.setStopExecution(true);
        TestStepExecutor executor = new TestStepExecutor();
        executor.send(asyncData,
                false);
        short tryCount = 0;
        while (result == null && tryCount < 6) {
            result = TestStepExecutor.getResultReciever(true).recieve(
                    asyncData.getMessageId(),
                    5000);

            tryCount++;
        }
        asyncData.setAsynchCall(true);
        asyncData.setStopExecution(false);
        if (result == null) {
            stepResultType = stepResultType.TIMEOUT_UNHANDLED;
        } else {

            if (result.isCancelled()) {
                stepResultType = stepResultType.TIMEOUT;
            } else {
                stepResultType = result.getResultType();
            }
        }
        return stepResultType;

    }
}
